package service

import (
	"encoding/json"
	"errors"
	"fmt"
	"sort"
	"strconv"
	"strings"
	"time"

	"gitee.com/sansaniot/ssiot-core/facade/env"
	"github.com/tidwall/sjson"
	"github.com/xuri/excelize/v2"
	"gorm.io/gorm"

	"ssdevice/common/constant"
	cDto "ssdevice/common/dto"
	"ssdevice/common/enums"
	"ssdevice/common/utils"
	"ssdevice/internal/device/models"
	"ssdevice/internal/device/models/command"
	"ssdevice/internal/device/models/query"
)

// 添加软网关 - 添加服务
func ctrlSoftGatewayServe(dev *models.SysDev, svcCtrl enums.SvcCtrlEnum) error {
	//webUrl := env.ConfigOperator.GetString("service.device.sansan")
	//if len(webUrl) == 0 {
	//	return errors.New("微服务管理无法建立连接")
	//}
	//param := make(map[string]interface{})
	//switch svcCtrl {
	//case enums.Add:
	//	param["sn"] = dev.Sn
	//	param["arg"] = "-sn " + dev.Sn
	//	//param["svcType"] = "applet"
	//	//fallthrough
	////case enums.Modify:
	////param["name"] = dev.Name
	////param["instanceId"] = dev.Sn
	//case enums.Remove:
	//	//param["instanceId"] = dev.Sn
	//	param["sn"] = dev.Sn
	//default:
	//	return nil
	//}
	//header := make(map[string]string)
	//header["Authentication"] = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjIwMzkwMTc1MjYsIm9yaWdfaWF0IjoxNjc5MDE3NTI2LCJ1c2VybmFtZSI6ImFkbWluIn0.-BGpKJPVLGS-iAMz6VCeKqYhtKaor_zaa-tbOgImTII"
	//var resultJson interface{}
	//err := utils.HttpPostJson(webUrl+"/api/service/app/soft-gateway/"+svcCtrl.Get().Cmd, param, header, &resultJson)
	//if err != nil {
	//	return errors.New("微服务管理接口请求失败," + err.Error())
	//}
	//if resultJson == nil {
	//	return errors.New("微服务管理接口请求未响应")
	//}
	//resp := resultJson.(map[string]interface{})
	//if resp["code"] == nil || resp["code"].(float64) != 200 {
	//	tmpErr := errors.New("微服务管理接口请求异常:" + resp["message"].(string))
	//	return tmpErr
	//}
	return nil
}

// 获取产品关联的设备SN
func getProductRelateSns(tx *gorm.DB, productIds, snList *[]string) error {
	dev := &models.SysDev{}
	devProduct := &models.SysDevProduct{}
	dev.Orm = tx
	devProduct.Orm = tx
	devList := make([]models.SysDev, 0)
	productList := make([]models.SysDevProduct, 0)
	if err := devProduct.FindList(&productList, cDto.MakeCondition(query.SysProductQuery{IdIn: *productIds})); err != nil {
		return err
	}
	productModels := make([]string, 0)
	for _, v := range productList {
		productModels = append(productModels, v.ProductModel)
	}
	if err := dev.FindList(&devList, cDto.MakeCondition(query.SysDevQuery{ProductModelIn: productModels})); err != nil {
		return err
	}
	for _, v := range devList {
		*snList = append(*snList, v.Sn)
	}
	return nil
}

// 切换产品模型
func changeDevProduct(tx *gorm.DB, dev *models.SysDev, toModel string) (err error) {
	// 目标产品
	prodM := &models.SysDevProduct{}
	prodM.Orm = tx
	if err = prodM.FindOne(prodM, cDto.MakeCondition(query.SysProductQuery{ProductModel: toModel})); err != nil {
		return
	}
	//if dev.Type != prodM.DeviceType {
	//	return errors.New("设备类型与所选产品不一致")
	//}
	// 不再校验不一致  直接取产品的类型
	if len(prodM.DeviceType) > 0 {
		dev.Type = prodM.DeviceType
	} else {
		return errors.New("产品类型信息异常")
	}
	dev.ProductModel = toModel
	schemaM := &models.SysDevSchema{}
	configM := &models.SysDevConfig{}
	sensorM := &models.SysDevSensor{}
	systemM := &models.SysDevSystem{}
	varibM := &models.SysDevVariable{}
	schemaM.Orm = tx
	configM.Orm = tx
	sensorM.Orm = tx
	systemM.Orm = tx
	varibM.Orm = tx
	// 先删后加，直接覆盖替换
	// 设备schema
	if err = schemaM.Delete(schemaM, cDto.MakeCondition(query.SysDevSchemaQuery{Sn: dev.Sn})); err != nil {
		return
	}
	// 设备profile.config
	if err = configM.Delete(configM, cDto.MakeCondition(query.SysDevConfigQuery{Sn: dev.Sn})); err != nil {
		return
	}
	// 设备profile.sensor
	if err = sensorM.Delete(sensorM, cDto.MakeCondition(query.SysDevSensorQuery{Sn: dev.Sn})); err != nil {
		return
	}
	// 设备profile.system
	if err = systemM.Delete(systemM, cDto.MakeCondition(query.SysDevSystemQuery{Sn: dev.Sn})); err != nil {
		return
	}
	// 设备变量
	if err = varibM.Delete(varibM, cDto.MakeCondition(query.SysDevVariableQuery{Sn: dev.Sn})); err != nil {
		return
	}
	// 根据产品生成
	if err = schemaM.FindOne(schemaM, cDto.MakeCondition(query.SysDevSchemaQuery{ProductId: prodM.Id})); err != nil {
		return
	}
	if err = configM.FindOne(configM, cDto.MakeCondition(query.SysDevConfigQuery{ProductId: prodM.Id})); err != nil {
		return
	}
	now := cDto.LocalTime(time.Now())
	// 复制成设备schema
	schemaM.Id = utils.SimpleUUID()
	schemaM.DevSn = dev.Sn
	schemaM.CreateTime = &now
	schemaM.UpdateTime = &now
	schemaM.ProductModel = ""
	schemaM.DevProductId = ""
	devConfig, devSensor, devSystem := generateProfile(tx, dev, *configM, &command.DeviceInsertReq{})
	devConfig.Orm = tx
	devSensor.Orm = tx
	devSystem.Orm = tx
	if err = schemaM.Add(schemaM); err != nil {
		return err
	}
	// 设备profile.config
	if err = devConfig.Add(devConfig); err != nil {
		return err
	}
	// 设备profile.sensor
	if err = devSensor.Add(devSensor); err != nil {
		return err
	}
	// 设备profile.system
	if err = devSystem.Add(devSystem); err != nil {
		return err
	}
	// 设备变量
	varKeyMap := GetProductVarKeyIndex(tx, toModel)
	vars := varibM.GenFromSchemaProfile(dev.Sn, utils.BytesToMap(schemaM.Schema), utils.BytesToMap(devConfig.Data), varKeyMap)
	if len(vars) > 0 {
		if err = varibM.Add(&vars); err != nil {
			return err
		}
	}
	return
}

// 查询历史数据(单/多设备)
func processHistoryData(db *gorm.DB, req *query.DevDataQuery, devSns []string) (err error, result interface{}) {
	// 确定sensor/system表
	keySplit := strings.Split(req.Key, ".")
	rootKey := keySplit[0]
	tableName := "sys_history_" + req.KeyType
	exist := db.Migrator().HasTable(tableName)
	if !exist {
		err = errors.New("查询历史数据,表不存在")
		return
	}
	whereSql := "dev_sn in ? and key = ?"
	cond := make([]interface{}, 0)
	cond = append(cond, devSns)
	cond = append(cond, rootKey)
	// 值不为空
	if req.HasValue {
		whereSql += " and value is not null and value != '' "
	}
	if len(req.StartTime) > 0 {
		whereSql += " and update_time>=?"
		cond = append(cond, req.StartTime)
	}
	if len(req.EndTime) > 0 {
		whereSql += " and update_time<=?"
		cond = append(cond, req.EndTime)
	}
	if len(req.Value) > 0 {
		whereSql += " and value like ?"
		cond = append(cond, "%"+req.Value+"%")
	}
	// 默认增序
	timeOrder := utils.If(strings.ToUpper(req.TimeOrder) == "DESC", "DESC", "ASC").(string)
	// 不传时间与分页，默认查询最新1000条记录
	defaultLimit := len(req.StartTime) == 0 && len(req.EndTime) == 0 && !(req.PageIndex > 0 && req.PageSize > 0)
	if defaultLimit {
		whereSql += " ORDER BY update_time DESC limit 1000 "
	}
	// db拼接
	db = db.Table(tableName).Where(whereSql, cond...)
	if !defaultLimit {
		db = db.Order(`"update_time" ` + timeOrder + " NULLS LAST")
	}
	// 分页or列表
	var resultList []map[string]interface{}
	var count int64
	if req.PageIndex > 0 && req.PageSize > 0 {
		offset := (req.PageIndex - 1) * req.PageSize
		if offset < 0 {
			offset = 0
		}
		if err = db.Count(&count).Error; err != nil {
			return
		}
		if err = db.Limit(req.PageSize).Offset(offset).Scan(&resultList).Error; err != nil {
			return
		}
	} else {
		if err = db.Scan(&resultList).Error; err != nil {
			return
		}
	}
	// 设备名称
	snNameMapper := devname()
	// 处理
	for _, v := range resultList {
		sn := v["dev_sn"].(string)
		v["devSn"] = sn
		v["devName"] = snNameMapper[sn]
		v["updateTime"] = utils.FormatTimeToStr(v["update_time"].(time.Time))
		if v["value"] != nil {
			valueStr := v["value"].(string)
			if strings.HasPrefix(valueStr, "{") && strings.HasSuffix(valueStr, "}") {
				valueObj := utils.JsonStrToMap(valueStr)
				v["value"] = valueObj
				if len(keySplit) > 1 {
					v["value"] = utils.GetFieldFromJson(keySplit[1:], valueObj)
				}
			} else if len(keySplit) > 1 {
				v["value"] = nil
			}
		}
		delete(v, "dev_sn")
		delete(v, "update_time")
	}
	// 矫正顺序
	if defaultLimit {
		sort.Slice(resultList, func(i, j int) bool {
			if timeOrder == "ASC" {
				return resultList[i]["updateTime"].(string) < resultList[j]["updateTime"].(string)
			} else {
				return resultList[i]["updateTime"].(string) >= resultList[j]["updateTime"].(string)
			}
		})
	}
	// 返回结果
	if req.PageIndex > 0 && req.PageSize > 0 {
		result = &cDto.Page{
			Records:   resultList,
			Total:     count,
			PageSize:  req.GetPageSize(),
			PageIndex: req.GetPageIndex(),
			Pages:     (count + int64(req.GetPageSize()) - 1) / int64(req.GetPageSize()),
			Other:     nil,
		}
	} else {
		result = resultList
	}
	return
}

// 查询历史数据(多变量。数据量比较大，会简化相关数据字段)
func getHistoryData(db *gorm.DB, req *query.DevDataQuery) (err error, result interface{}) {
	// 确定sensor/system表
	keys := strings.Split(req.Key, ",")
	tableName := "sys_history_" + req.KeyType
	exist := db.Migrator().HasTable(tableName)
	if !exist {
		err = errors.New("查询历史数据,表不存在")
		return
	}
	whereSql := "dev_sn = ? and key in ?"
	cond := make([]interface{}, 0)
	cond = append(cond, req.IdSn)
	cond = append(cond, keys)
	// 值不为空
	if req.HasValue {
		whereSql += " and value is not null and value != '' "
	}
	if len(req.StartTime) > 0 {
		whereSql += " and update_time>=?"
		cond = append(cond, req.StartTime)
	}
	if len(req.EndTime) > 0 {
		whereSql += " and update_time<=?"
		cond = append(cond, req.EndTime)
	}
	if len(req.Value) > 0 {
		whereSql += " and value like ?"
		cond = append(cond, "%"+req.Value+"%")
	}
	// 默认增序
	timeOrder := utils.If(strings.ToUpper(req.TimeOrder) == "DESC", "DESC", "ASC").(string)
	// 不传时间与分页，默认查询最新1000条记录
	defaultLimit := len(req.StartTime) == 0 && len(req.EndTime) == 0 && !(req.PageIndex > 0 && req.PageSize > 0)
	if defaultLimit {
		whereSql += " ORDER BY update_time DESC limit 1000 "
	}
	// db拼接
	db = db.Table(tableName).Where(whereSql, cond...)
	if !defaultLimit {
		db = db.Order(`"update_time" ` + timeOrder + " NULLS LAST")
	}
	// 分页or列表
	var resultList []map[string]interface{}
	var count int64
	if req.PageIndex > 0 && req.PageSize > 0 {
		offset := (req.PageIndex - 1) * req.PageSize
		if offset < 0 {
			offset = 0
		}
		if err = db.Count(&count).Error; err != nil {
			return
		}
		if err = db.Limit(req.PageSize).Offset(offset).Scan(&resultList).Error; err != nil {
			return
		}
	} else {
		if err = db.Select([]string{"key", "value", "update_time"}).Scan(&resultList).Error; err != nil {
			return
		}
	}
	// 返回结果
	if req.PageIndex > 0 && req.PageSize > 0 {
		result = &cDto.Page{
			Records:   resultList,
			Total:     count,
			PageSize:  req.GetPageSize(),
			PageIndex: req.GetPageIndex(),
			Pages:     (count + int64(req.GetPageSize()) - 1) / int64(req.GetPageSize()),
			Other:     nil,
		}
	} else {
		result = resultList
	}
	return
}

func devname() map[string]string {
	m := make(map[string]string)
	devcache, _ := env.Cache.HashGetAll(constant.DeviceEasyCache)
	for sn, v := range devcache {
		info := utils.BytesToMap([]byte(v))
		name := utils.If(info["name"] != nil, info["name"], "").(string)
		m[sn] = name
	}
	return m
}

// schema转变量表
func devSchemaVariable(db *gorm.DB, sn string) error {
	devSchemaModel := models.SysDevSchema{}
	devSchemaModel.Orm = db
	err, devSchema := devSchemaModel.FindDevSchema(&query.SysDevSchemaQuery{Sn: sn})
	if err != nil {
		return err
	}
	devConfig := &models.SysDevConfig{}
	devConfig.Orm = db
	if err = devConfig.FindDevConfigBySn(sn); err != nil {
		return err
	}
	varKeyMap := GetDevVarKeyIndex(db, sn)
	variableModel := models.SysDevVariable{}
	variableModel.Orm = db
	//vars := variableModel.GenFromSchemaProfile(sn, utils.BytesToMap(devSchema.Schema), map[string]interface{}{})
	vars := variableModel.GenFromSchemaProfile(sn, utils.BytesToMap(devSchema.Schema), utils.BytesToMap(devConfig.Data), varKeyMap)
	if err = variableModel.Delete(&models.SysDevVariable{}, cDto.MakeCondition(query.SysDevVariableQuery{Sn: sn})); err != nil {
		return err
	}
	if len(vars) > 0 {
		if err = variableModel.UpsertDoUpdate(&vars, []string{"sn", "key"}, []string{"label", "type", "config", "index", "value"}); err != nil {
			return err
		}
	}
	return nil
}

// profile转变量表
func devProfileVariable(db *gorm.DB, sn string) error {
	devSchemaModel := &models.SysDevSchema{}
	devSchemaModel.Orm = db
	err, devSchema := devSchemaModel.FindDevSchema(&query.SysDevSchemaQuery{Sn: sn})
	if err != nil {
		return err
	}
	devConfig := &models.SysDevConfig{}
	devConfig.Orm = db
	if err = devConfig.FindDevConfigBySn(sn); err != nil {
		return err
	}
	varKeyMap := GetDevVarKeyIndex(db, sn)
	variableModel := models.SysDevVariable{}
	variableModel.Orm = db
	vars := variableModel.GenFromSchemaProfile(sn, utils.BytesToMap(devSchema.Schema), utils.BytesToMap(devConfig.Data), varKeyMap)
	if err = variableModel.Delete(&models.SysDevVariable{}, cDto.MakeCondition(query.SysDevVariableQuery{Sn: sn})); err != nil {
		return err
	}
	if len(vars) > 0 {
		if err = variableModel.UpsertDoUpdate(&vars, []string{"sn", "key"}, []string{"label", "type", "config", "index", "value"}); err != nil {
			return err
		}
	}
	return nil
}

func vs(variableObj interface{}) map[string]interface{} {
	varKey, varType, varLabel := "", "", ""
	if devVariable, ok := variableObj.(models.SysDevVariable); ok {
		varKey, varType, varLabel = devVariable.Key, devVariable.Type, devVariable.Label
	} else if pdVariable, ok := variableObj.(models.SysProductVariable); ok {
		varKey, varType, varLabel = pdVariable.Key, pdVariable.Type, pdVariable.Label
	}
	tmpData := map[string]interface{}{}
	splitKey := strings.Split(varKey, ".") // 11.a.b.c
	for i, s := range splitKey {
		path := ""
		if i == 0 {
			path = s
		} else {
			for j := 0; j <= i; j++ {
				path += splitKey[j]
				if j < i {
					path += ".properties."
				}
			}
		}
		ds := string(utils.MapToBytes(tmpData))
		if i < len(splitKey)-1 {
			ds, _ = sjson.Set(ds, path, map[string]interface{}{
				"type":       "object",
				"properties": map[string]interface{}{},
			})
		} else {
			ds, _ = sjson.Set(ds, path, map[string]interface{}{
				"type":  varType,
				"title": varLabel,
			})
		}
		tmpData = utils.BytesToMap([]byte(ds))
	}
	return tmpData
}

func vp(variableObj interface{}) (tmpData, schemaConfig map[string]interface{}) {
	var varKey string
	var varConfig []byte
	if devVariable, ok := variableObj.(models.SysDevVariable); ok {
		varKey, varConfig = devVariable.Key, devVariable.Config
	} else if pdVariable, ok := variableObj.(models.SysProductVariable); ok {
		varKey, varConfig = pdVariable.Key, pdVariable.Config
	}
	tmpData = map[string]interface{}{}
	splitKey := strings.Split(varKey, ".")
	for i, s := range splitKey {
		path := ""
		if i == 0 {
			path = s
		} else {
			for j := 0; j <= i; j++ {
				path += splitKey[j]
				if j < i {
					path += "."
				}
			}
		}
		ds := string(utils.MapToBytes(tmpData))
		if i < len(splitKey)-1 {
			ds, _ = sjson.Set(ds, path, map[string]interface{}{})
		} else {
			ds, _ = sjson.Set(ds, path, utils.BytesToMap(varConfig))
		}
		tmpData = utils.BytesToMap([]byte(ds))
	}
	// schemaConfig
	if len(tmpData) > 0 {
		configSchema := utils.GetObjectSchema(tmpData)
		if configSchema["properties"] == nil {
			return
		}
		configSchema = configSchema["properties"].(map[string]interface{})
		// 各自拉平层级
		flattenConfigSchema := map[string]interface{}{}
		utils.FlattenJson(configSchema, flattenConfigSchema, []string{})
		// 还原拉平层级
		schemaConfig = utils.FixJsonKeyDot(flattenConfigSchema)
	}
	return
}

// 变量转schema
func devVariableSchema(db *gorm.DB, devVariable models.SysDevVariable) error {
	tmpData := vs(devVariable)
	toBytes := utils.MapToBytes(tmpData)
	paths := "{properties,sensor,properties}"
	dataStr := string(toBytes)
	sql := "update sys_dev_schema set schema = jsonb_set(schema::jsonb, '" + paths + "', jsonb_update(schema #> '" + paths + "', '" + dataStr + "' :: jsonb ) ) "
	where := " where dev_sn = '%s' "
	// 防止字符串中存在?占位符
	sql += fmt.Sprintf(where, devVariable.Sn)
	if err := db.Raw(sql).Scan(&models.SysDevSchema{}).Error; err != nil {
		return err
	}
	return nil
}

// 变量转profile
func devVariableProfile(db *gorm.DB, devVariable models.SysDevVariable) error {
	tmpData, schemaConfig := vp(devVariable)
	path := "{config,sensor}"
	dataStr := string(utils.MapToBytes(tmpData))
	sql := "update sys_dev_config set data = jsonb_set(data::jsonb, '" + path + "', jsonb_update(data #> '" + path + "', '" + dataStr + "' :: jsonb ) ) "
	where := " where dev_sn = '%s' "
	// 防止字符串中存在?占位符
	sql += fmt.Sprintf(where, devVariable.Sn)
	if err := db.Raw(sql).Scan(&models.SysDevConfig{}).Error; err != nil {
		return err
	}
	if len(schemaConfig) > 0 {
		// 更新到schema的config
		toBytes := utils.MapToBytes(schemaConfig)
		paths := "{properties,config,properties,sensor,properties}"
		dataStr = string(toBytes)
		sql = "update sys_dev_schema set schema = jsonb_set(schema::jsonb, '" + paths + "', jsonb_update(schema #> '" + paths + "', '" + dataStr + "' :: jsonb ) ) "
		where = " where dev_sn = '%s' "
		// 防止字符串中存在?占位符
		sql += fmt.Sprintf(where, devVariable.Sn)
		if err := db.Raw(sql).Scan(&models.SysDevSchema{}).Error; err != nil {
			return err
		}
	}
	return nil
}

// schema转变量表
func prodSchemaVariable(db *gorm.DB, productModel string) error {
	devSchemaModel := models.SysDevSchema{}
	devSchemaModel.Orm = db
	err, devSchema := devSchemaModel.FindDevSchema(&query.SysDevSchemaQuery{ProductModel: productModel})
	if err != nil {
		return err
	}
	devConfig := &models.SysDevConfig{}
	devConfig.Orm = db
	if err = devConfig.FindDevConfigByPdModel(productModel); err != nil {
		return err
	}
	varKeyMap := GetProductVarKeyIndex(db, productModel)
	variableModel := models.SysProductVariable{}
	variableModel.Orm = db
	//vars := variableModel.GenFromSchemaProfile(productModel, utils.BytesToMap(devSchema.Schema), map[string]interface{}{})
	vars := variableModel.GenFromSchemaProfile(productModel, utils.BytesToMap(devSchema.Schema), utils.BytesToMap(devConfig.Data), varKeyMap)
	if err = variableModel.Delete(&models.SysProductVariable{}, cDto.MakeCondition(query.SysProductVariableQuery{ProductModel: productModel})); err != nil {
		return err
	}
	if len(vars) > 0 {
		if err = variableModel.UpsertDoUpdate(&vars, []string{"product_model", "key"}, []string{"label", "type", "config", "index"}); err != nil {
			return err
		}
	}
	return nil
}

// profile转变量表
func prodProfileVariable(db *gorm.DB, productModel string) error {
	devSchemaModel := &models.SysDevSchema{}
	devSchemaModel.Orm = db
	err, devSchema := devSchemaModel.FindDevSchema(&query.SysDevSchemaQuery{ProductModel: productModel})
	if err != nil {
		return err
	}
	devConfig := &models.SysDevConfig{}
	devConfig.Orm = db
	if err = devConfig.FindDevConfigByPdModel(productModel); err != nil {
		return err
	}
	variableModel := models.SysProductVariable{}
	variableModel.Orm = db
	varKeyMap := GetProductVarKeyIndex(db, productModel)
	vars := variableModel.GenFromSchemaProfile(productModel, utils.BytesToMap(devSchema.Schema), utils.BytesToMap(devConfig.Data), varKeyMap)
	if err = variableModel.Delete(&models.SysProductVariable{}, cDto.MakeCondition(query.SysProductVariableQuery{ProductModel: productModel})); err != nil {
		return err
	}
	if len(vars) > 0 {
		if err = variableModel.UpsertDoUpdate(&vars, []string{"product_model", "key"}, []string{"label", "type", "config", "index"}); err != nil {
			return err
		}
	}
	return nil
}

// 变量转schema
func prodVariableSchema(db *gorm.DB, productVariable models.SysProductVariable) error {
	tmpData := vs(productVariable)
	toBytes := utils.MapToBytes(tmpData)
	paths := "{properties,sensor,properties}"
	dataStr := string(toBytes)
	sql := "update sys_dev_schema set schema = jsonb_set(schema::jsonb, '" + paths + "', jsonb_update(schema #> '" + paths + "', '" + dataStr + "' :: jsonb ) ) "
	where := " where product_model = '%s' "
	// 防止字符串中存在?占位符
	sql += fmt.Sprintf(where, productVariable.ProductModel)
	if err := db.Raw(sql).Scan(&models.SysDevSchema{}).Error; err != nil {
		return err
	}
	return nil
}

// 变量转profile
func prodVariableProfile(db *gorm.DB, productVariable models.SysProductVariable) error {
	tmpData, schemaConfig := vp(productVariable)
	path := "{config,sensor}"
	dataStr := string(utils.MapToBytes(tmpData))
	sql := "update sys_dev_config set data = jsonb_set(data::jsonb, '" + path + "', jsonb_update(data #> '" + path + "', '" + dataStr + "' :: jsonb ) ) "
	where := " where product_model = '%s' "
	// 防止字符串中存在?占位符
	sql += fmt.Sprintf(where, productVariable.ProductModel)
	if err := db.Raw(sql).Scan(&models.SysDevConfig{}).Error; err != nil {
		return err
	}
	if len(schemaConfig) > 0 {
		// 更新到schema的config
		toBytes := utils.MapToBytes(schemaConfig)
		paths := "{properties,config,properties,sensor,properties}"
		dataStr = string(toBytes)
		sql = "update sys_dev_schema set schema = jsonb_set(schema::jsonb, '" + paths + "', jsonb_update(schema #> '" + paths + "', '" + dataStr + "' :: jsonb ) ) "
		where = " where product_model = '%s' "
		// 防止字符串中存在?占位符
		sql += fmt.Sprintf(where, productVariable.ProductModel)
		if err := db.Raw(sql).Scan(&models.SysDevSchema{}).Error; err != nil {
			return err
		}
	}
	return nil
}

var (
	VarHeaderName = []string{"设备", "变量名称", "变量标识", "数据类型", "实时值", "更新时间", "变量配置", "扩展信息"}
	VarHeaderKey  = []string{"sn", "label", "key", "type", "value", "updateTime", "config", "param"}
)

// 导出变量内容填充
func FillExportVariable(f *excelize.File, vars []models.SysDevVariable) error {
	// 头部单元格样式
	headerCellStyle, _ := f.NewStyle(&excelize.Style{
		Font: &excelize.Font{
			Bold: true,
		},
		Alignment: &excelize.Alignment{
			Horizontal: "center",
			Vertical:   "center",
		},
		Border: []excelize.Border{
			{Type: "left", Color: "000000", Style: 1},
			{Type: "top", Color: "000000", Style: 1},
			{Type: "bottom", Color: "000000", Style: 1},
			{Type: "right", Color: "000000", Style: 1},
		},
	})
	sheetName := "设备变量"
	_ = f.NewSheet(sheetName)
	// 第1行
	_ = f.InsertRow(sheetName, 1)
	colNum := len(VarHeaderName)
	for n := 0; n < colNum; n++ {
		axis, _ := excelize.ColumnNumberToName(n + 1)
		_ = f.SetCellValue(sheetName, axis+"1", VarHeaderName[n])
		_ = f.SetCellStyle(sheetName, axis+"1", axis+"1", headerCellStyle)
		_ = f.SetColWidth(sheetName, axis, axis, 20)
	}
	// 数据行起始
	startRow := 2
	size := len(VarHeaderKey)
	// 填充已有数据模型
	varSize := len(vars)
	for row := startRow; row-startRow < varSize; row++ {
		_ = f.InsertRow(sheetName, row)
		devVariable := vars[row-startRow]
		rowStr := strconv.Itoa(row)
		for colIndex := 0; colIndex < size; colIndex++ {
			key := VarHeaderKey[colIndex]
			axis, _ := excelize.ColumnNumberToName(colIndex + 1)
			switch key {
			case "sn":
				_ = f.SetCellValue(sheetName, axis+rowStr, devVariable.Sn)
				break
			case "label":
				_ = f.SetCellValue(sheetName, axis+rowStr, devVariable.Label)
				break
			case "key":
				_ = f.SetCellValue(sheetName, axis+rowStr, devVariable.Key)
				break
			case "type":
				_ = f.SetColWidth(sheetName, axis, axis, 10)
				_ = f.SetCellValue(sheetName, axis+rowStr, devVariable.Type)
				break
			case "value":
				_ = f.SetCellValue(sheetName, axis+rowStr, devVariable.Value)
				break
			case "updateTime":
				_ = f.SetColWidth(sheetName, axis, axis, 25)
				if devVariable.UpdateTime != nil {
					_ = f.SetCellValue(sheetName, axis+rowStr, utils.FormatTimeToStr(time.Time(*devVariable.UpdateTime)))
				}
				break
			case "config":
				_ = f.SetColWidth(sheetName, axis, axis, 40)
				_ = f.SetCellValue(sheetName, axis+rowStr, string(devVariable.Config))
				break
			case "param":
				_ = f.SetColWidth(sheetName, axis, axis, 40)
				_ = f.SetCellValue(sheetName, axis+rowStr, string(devVariable.Param))
				break
			default:
				break
			}
		}
	}
	return nil
}

func ImportVariableData(variableModel *models.SysDevVariable, f *excelize.File, userId string) error {
	sheetNum := f.SheetCount
	if sheetNum == 0 {
		return nil
	}
	devModel := &models.SysDev{}
	devModel.Orm = variableModel.Orm
	existSns, snDevMap := devModel.GetDevSnList()
	// 解析
	vars := make([]models.SysDevVariable, 0)
	errorInfo := ""
	for _, sheetName := range f.GetSheetList() {
		// 遍历设备
		rowIndexRead := make([]int, 0)
		sheetErrPre := fmt.Sprintf("<br>(sheet: %s)<br>", sheetName)
		headerNames := VarHeaderName
		headerKeys := VarHeaderKey
		// 按行获取[][]单元格值，f.Rows迭代器不能过滤掉空内容的行
		rows, err := f.GetRows(sheetName)
		if err != nil {
			fmt.Println(err)
		}
		cols, err := f.GetCols(sheetName)
		if err != nil {
			fmt.Println(err)
		}
		maxRowNumber := len(rows)
		maxColNumber := len(cols)
		if maxRowNumber <= 1 {
			errorInfo += fmt.Sprintf("模板内容为空(sheet:%s)", sheetName)
			continue
		}
		if maxColNumber != len(headerNames) {
			return errors.New("无效的导入文件，请重新导出文件填写内容")
		}
		for rowIndex, rowColumns := range rows {
			if utils.IntIn(rowIndex, rowIndexRead) {
				// 效率优化 (每sheet中 遍历过的行跳过)
				continue
			}
			if rowIndex == 0 {
				rowIndexRead = append(rowIndexRead, rowIndex)
				continue
			}
			// 第2行起 为数据内容 解析行数据
			rowDataParseDto := parseRowData(rowColumns, headerKeys, existSns, snDevMap, userId)
			if rowDataParseDto.NotSn {
				continue
			}
			rowIndexRead = append(rowIndexRead, rowIndex)
			errMsg := rowDataParseDto.Error
			if len(errMsg) > 0 {
				tmpErrStr := fmt.Sprintf("第%s行:<br>%s", strconv.Itoa(rowIndex+1), errMsg)
				if !strings.Contains(errorInfo, sheetErrPre) {
					errorInfo += sheetErrPre
				}
				if !strings.Contains(errorInfo, tmpErrStr) {
					errorInfo += tmpErrStr
				}
				continue
			}
			devVariable := rowDataParseDto.SysDevVariable
			vars = append(vars, devVariable)
		}
	}
	if len(errorInfo) > 0 {
		return errors.New(errorInfo)
	}
	if len(vars) == 0 {
		return errors.New("变量无更新或已被清理")
	}
	// 按sn聚合
	snVars := make(map[string][]models.SysDevVariable)
	for _, devVariable := range vars {
		if _, ok := snVars[devVariable.Sn]; !ok {
			snVars[devVariable.Sn] = make([]models.SysDevVariable, 0)
		}
		snVars[devVariable.Sn] = append(snVars[devVariable.Sn], devVariable)
	}
	if err := variableModel.Orm.Transaction(func(tsx *gorm.DB) error {
		variableModel.Orm = tsx
		// 保存/更新
		if err := variableModel.Upsert(&vars, []string{"sn", "key"}...); err != nil {
			return err
		}
		for sn, variables := range snVars {
			snSchemaSensor := map[string]interface{}{}
			snSchemaConfig := map[string]interface{}{}
			snProfile := map[string]interface{}{}
			for _, devVar := range variables {
				pc, scc := vp(devVar)
				utils.DeepMerge(vs(devVar), snSchemaSensor)
				utils.DeepMerge(scc, snSchemaConfig)
				utils.DeepMerge(pc, snProfile)
			}
			// schema
			toBytes := utils.MapToBytes(snSchemaSensor)
			paths := "{properties,sensor,properties}"
			dataStr := string(toBytes)
			sql := "update sys_dev_schema set schema = jsonb_set(schema::jsonb, '" + paths + "', jsonb_update(schema #> '" + paths + "', '" + dataStr + "' :: jsonb ) ) "
			where := " where dev_sn = '%s' "
			sql += fmt.Sprintf(where, sn)
			if err := tsx.Raw(sql).Scan(&models.SysDevSchema{}).Error; err != nil {
				return err
			}
			// schema config
			toBytes = utils.MapToBytes(snSchemaConfig)
			paths = "{properties,config,properties,sensor,properties}"
			dataStr = string(toBytes)
			sql = "update sys_dev_schema set schema = jsonb_set(schema::jsonb, '" + paths + "', jsonb_update(schema #> '" + paths + "', '" + dataStr + "' :: jsonb ) ) "
			where = " where dev_sn = '%s' "
			// 防止字符串中存在?占位符
			sql += fmt.Sprintf(where, sn)
			if err := tsx.Raw(sql).Scan(&models.SysDevSchema{}).Error; err != nil {
				return err
			}
			// profile
			paths = "{config,sensor}"
			dataStr = string(utils.MapToBytes(snProfile))
			sql = "update sys_dev_config set data = jsonb_set(data::jsonb, '" + paths + "', jsonb_update(data #> '" + paths + "', '" + dataStr + "' :: jsonb ) ) "
			where = " where dev_sn = '%s' "
			// 防止字符串中存在?占位符
			sql += fmt.Sprintf(where, sn)
			if err := tsx.Raw(sql).Scan(&models.SysDevConfig{}).Error; err != nil {
				return err
			}
		}
		return nil
	}); err != nil {
		return err
	}
	return nil
}

type RowDataParseDto struct {
	SysDevVariable models.SysDevVariable
	Error          string
	NotSn          bool
}

// 导入变量 - 解析行数据
func parseRowData(rowColumns, headerKeys, existSns []string, snDevMap map[string]models.SysDev, userId string) (rowDataParseDto RowDataParseDto) {
	errStr := ""
	devVariable := models.SysDevVariable{}
	maxCol := len(headerKeys)
	length := len(rowColumns)
	for colIndex := 0; colIndex < maxCol; colIndex++ {
		headerKey := headerKeys[colIndex]
		cellVal := ""
		if colIndex < length {
			cellVal = rowColumns[colIndex]
		}
		cellVal = strings.TrimSpace(cellVal)
		axis, _ := excelize.ColumnNumberToName(colIndex + 1)
		switch headerKey {
		case "sn":
			if len(strings.ReplaceAll(cellVal, " ", "")) == 0 {
				errStr += fmt.Sprintf("%s列-%s<br>", axis, "设备SN(必填)")
				break
			}
			if !utils.StrIn(cellVal, existSns) {
				// 忽略不存在的设备
				rowDataParseDto.NotSn = true
				return
			}
			if snDevMap[cellVal].DeptId == constant.PublicGroupId && userId != "1" {
				errStr += fmt.Sprintf("%s列-%s<br>", axis, "没权限操作公共分组资源")
				break
			}
			devVariable.Sn = cellVal
			break
		case "label":
			devVariable.Label = cellVal
			break
		case "key":
			if len(strings.ReplaceAll(cellVal, " ", "")) == 0 {
				errStr += fmt.Sprintf("%s列-%s<br>", axis, "变量标识(必填)")
				break
			}
			devVariable.Key = cellVal
			break
		case "type":
			devVariable.Type = cellVal
			break
		case "value":
			devVariable.Value = cellVal
			break
		case "updateTime":
			break
		case "config":
			cellVal = strings.ReplaceAll(cellVal, " ", "")
			if len(cellVal) == 0 {
				break
			}
			var m = make(map[string]interface{})
			if err := json.Unmarshal([]byte(cellVal), &m); err != nil {
				errStr += fmt.Sprintf("%s列-%s<br>", axis, "变量配置内容非法")
				break
			}
			devVariable.Config = utils.MapToBytes(m)
			break
		case "param":
			cellVal = strings.ReplaceAll(cellVal, " ", "")
			if len(cellVal) == 0 {
				break
			}
			var m = make(map[string]interface{})
			if err := json.Unmarshal([]byte(cellVal), &m); err != nil {
				errStr += fmt.Sprintf("%s列-%s<br>", axis, "变量扩展信息非法")
				break
			}
			devVariable.Param = utils.MapToBytes(m)
			break
		default:
			break
		}
	}
	if len(devVariable.Label) == 0 {
		devVariable.Label = devVariable.Key
	}
	// 返回行解析
	if len(errStr) == 0 && len(devVariable.Sn) > 0 && len(devVariable.Key) > 0 {
		// 数据组装返回
		rowDataParseDto = RowDataParseDto{
			SysDevVariable: devVariable,
		}
	} else if len(errStr) > 0 {
		// 错误信息返回
		rowDataParseDto = RowDataParseDto{
			Error: errStr,
		}
	}
	return
}

func GetDevVarKeyIndex(db *gorm.DB, sn string) map[string]map[string]interface{} {
	vars := make([]models.SysDevVariable, 0)
	devVarM := &models.SysDevVariable{}
	devVarM.Orm = db
	_ = devVarM.FindList(&vars, cDto.MakeCondition(query.SysDevVariableQuery{Sn: sn}))

	result := make(map[string]map[string]interface{})
	keyIndex := make(map[string]int)
	keyValue := make(map[string]string)
	keyTime := make(map[string]cDto.LocalTime)
	keyParam := make(map[string]string)
	for _, v := range vars {
		if v.Index != nil {
			keyIndex[v.Key] = *v.Index
		}
		keyValue[v.Key] = v.Value
		if v.UpdateTime != nil {
			keyTime[v.Key] = *v.UpdateTime
		}
		if len(v.Param) > 0 {
			keyParam[v.Key] = string(v.Param)
		}
	}
	for k, idx := range keyIndex {
		if _, ok := result[k]; !ok {
			result[k] = make(map[string]interface{})
		}
		result[k]["index"] = idx
	}
	for k, v := range keyValue {
		if _, ok := result[k]; !ok {
			result[k] = make(map[string]interface{})
		}
		result[k]["value"] = v
	}
	for k, tm := range keyTime {
		if _, ok := result[k]; !ok {
			result[k] = make(map[string]interface{})
		}
		result[k]["updateTime"] = tm
	}
	for k, par := range keyParam {
		if _, ok := result[k]; !ok {
			result[k] = make(map[string]interface{})
		}
		result[k]["param"] = par
	}
	return result
}

func GetProductVarKeyIndex(db *gorm.DB, pdmodel string) map[string]map[string]interface{} {
	vars := make([]models.SysProductVariable, 0)
	pdVarM := &models.SysProductVariable{}
	pdVarM.Orm = db
	_ = pdVarM.FindList(&vars, cDto.MakeCondition(query.SysProductVariableQuery{ProductModel: pdmodel}))
	result := make(map[string]map[string]interface{})
	keyIndex := make(map[string]int)
	keyParam := make(map[string]string)
	for _, v := range vars {
		if v.Index != nil {
			keyIndex[v.Key] = *v.Index
		}
		if len(v.Param) > 0 {
			keyParam[v.Key] = string(v.Param)
		}
	}
	for k, idx := range keyIndex {
		if _, ok := result[k]; !ok {
			result[k] = make(map[string]interface{})
		}
		result[k]["index"] = idx
	}
	for k, par := range keyParam {
		if _, ok := result[k]; !ok {
			result[k] = make(map[string]interface{})
		}
		result[k]["param"] = par
	}
	return result
}

func preDevPdModel(db *gorm.DB, req *command.DeviceInsertReq) (err error) {
	if !req.FromLoginInit {
		return
	}
	pd := &models.SysDevProduct{}
	pd.Orm = db
	if err = pd.FindOne(pd, cDto.MakeCondition(query.SysProductQuery{ProductModel: req.ProductModel})); err != nil {
		if !errors.Is(err, gorm.ErrRecordNotFound) {
			return
		}
		err = nil
	}
	if len(pd.Id) > 0 {
		return
	}
	// 设备接入model不存在 则自动创建一个产品
	pdservice := &Product{}
	pdservice.Orm = db
	pdservice.SetAllModel()
	devDeptFlag := len(req.DeptId) > 0
	fromLoginDefault := req.FromLoginInit && req.ProductModel == constant.DefaultProductCode
	deptId := utils.If(devDeptFlag, req.DeptId, constant.AnonymousGroupId).(string)
	if fromLoginDefault {
		deptId = constant.AnonymousGroupId
	}
	if err, _ = pdservice.AddProduct(&command.ProductInsertReq{
		Name:         req.ProductModel,
		DeviceType:   req.Type,
		ProductModel: req.ProductModel,
		DeptId:       deptId,
		DevSchema:    &models.SysDevSchema{Schema: []byte(constant.DevDefaultSchemaTemplate)},
		DevConfig:    &models.SysDevConfig{Data: []byte(constant.DevDefaultConfigTemplate)},
	}); err != nil {
		err = errors.New("自动创建设备接入model产品 发生异常 " + err.Error())
		return
	}
	return
}

func getProductMeta(db *gorm.DB, req *query.SysProductQuery) (content []byte, err error) {
	productModel := models.SysDevProduct{}
	productModel.Orm = db
	products := make([]models.SysDevProduct, 0)
	productQuery := query.SysProductQuery{}
	if len(req.ProductModelIn) > 0 {
		productQuery.ProductModelIn = req.ProductModelIn
	}
	_ = productModel.FindList(&products, cDto.MakeCondition(productQuery))
	pdModels := make([]string, 0)
	pdIds := make([]string, 0)
	for _, pd := range products {
		pdModels = append(pdModels, pd.ProductModel)
		pdIds = append(pdIds, pd.Id)
	}
	if len(pdModels) == 0 {
		err = errors.New("查无数据")
		return
	}
	// 属性
	schemas := make([]models.SysDevSchema, 0)
	configs := make([]models.SysDevConfig, 0)
	variables := make([]models.SysProductVariable, 0)
	schemaModel := models.SysDevSchema{}
	configModel := models.SysDevConfig{}
	variableModel := models.SysProductVariable{}
	schemaModel.Orm = db
	configModel.Orm = db
	variableModel.Orm = db
	if err = schemaModel.FindList(&schemas, cDto.MakeCondition(query.SysDevSchemaQuery{ProductIdIn: pdIds})); err != nil {
		return
	}
	if err = configModel.FindList(&configs, cDto.MakeCondition(query.SysDevConfigQuery{ProductIdIn: pdIds})); err != nil {
		return
	}
	if err = variableModel.FindList(&variables, cDto.MakeCondition(query.SysProductVariableQuery{ProductModelIn: pdModels})); err != nil {
		return
	}
	pdSchema := make(map[string]map[string]interface{})
	pdConfig := make(map[string]map[string]interface{})
	pdVariable := make(map[string][]map[string]interface{})
	for _, schema := range schemas {
		pdSchema[schema.ProductModel] = map[string]interface{}{
			"id":           schema.Id,
			"devSn":        schema.DevSn,
			"schema":       utils.BytesToMap(schema.Schema),
			"productModel": schema.ProductModel,
			"devProductId": schema.DevProductId,
		}
		if schema.CreateTime != nil {
			pdSchema[schema.ProductModel]["createTime"] = utils.FormatTimeToStr(time.Time(*schema.CreateTime))
		}
		if schema.UpdateTime != nil {
			pdSchema[schema.ProductModel]["updateTime"] = utils.FormatTimeToStr(time.Time(*schema.UpdateTime))
		}
	}
	for _, config := range configs {
		pdConfig[config.ProductModel] = map[string]interface{}{
			"id":           config.Id,
			"devSn":        config.DevSn,
			"data":         utils.BytesToMap(config.Data),
			"productModel": config.ProductModel,
			"devProductId": config.DevProductId,
		}
		if config.CreateTime != nil {
			pdConfig[config.ProductModel]["createTime"] = utils.FormatTimeToStr(time.Time(*config.CreateTime))
		}
		if config.UpdateTime != nil {
			pdConfig[config.ProductModel]["updateTime"] = utils.FormatTimeToStr(time.Time(*config.UpdateTime))
		}
	}
	for _, variable := range variables {
		if _, ok := pdVariable[variable.ProductModel]; !ok {
			pdVariable[variable.ProductModel] = make([]map[string]interface{}, 0)
		}
		tmp := map[string]interface{}{
			"productModel": variable.ProductModel,
			"key":          variable.Key,
			"label":        variable.Label,
			"value":        variable.Value,
			"type":         variable.Type,
			"config":       utils.BytesToMap(variable.Config),
			"param":        utils.BytesToMap(variable.Param),
			"index":        variable.Index,
		}
		if variable.ReportTime != nil {
			tmp["reportTime"] = utils.FormatTimeToStr(time.Time(*variable.ReportTime))
		}
		if variable.UpdateTime != nil {
			tmp["updateTime"] = utils.FormatTimeToStr(time.Time(*variable.UpdateTime))
		}
		pdVariable[variable.ProductModel] = append(pdVariable[variable.ProductModel], tmp)
	}
	// 生成结果
	result := make([]map[string]interface{}, 0)
	for _, pd := range products {
		schemaMap := pdSchema[pd.ProductModel]
		configMap := pdConfig[pd.ProductModel]
		variableMaps := pdVariable[pd.ProductModel]
		if len(schemaMap) == 0 || len(configMap) == 0 {
			continue
		}
		//if strings.HasPrefix(pd.Photo, "sansan/") {
		//	// 远程上传文件是admin的1/1
		//	pd.Photo = "sansan/1/1" + pd.Photo[strings.LastIndex(pd.Photo, "/"):]
		//}
		productMap := map[string]interface{}{
			"id":          pd.Id,
			"name":        pd.Name,
			"deviceType":  pd.DeviceType,
			"status":      pd.Status,
			"model":       pd.ProductModel,
			"deptId":      pd.DeptId,
			"photo":       pd.Photo,
			"description": pd.Description,
			"view":        utils.BytesToMap(pd.View),
		}
		if pd.CreateTime != nil {
			productMap["createTime"] = utils.FormatTimeToStr(time.Time(*pd.CreateTime))
		}
		if pd.UpdateTime != nil {
			productMap["updateTime"] = utils.FormatTimeToStr(time.Time(*pd.UpdateTime))
		}
		contentMap := map[string]interface{}{
			"product":  productMap,
			"schema":   schemaMap,
			"config":   configMap,
			"variable": variableMaps,
		}
		result = append(result, contentMap)
	}
	content = utils.MapArrToBytes(result)
	return
}

func importProductMeta(db *gorm.DB, contentMapArr []map[string]interface{}) (err error) {
	return db.Transaction(func(tx *gorm.DB) error {
		for _, contentInfo := range contentMapArr {
			// 数据内容
			pdInfo := contentInfo["product"].(map[string]interface{})
			schemaInfo := contentInfo["schema"].(map[string]interface{})
			configInfo := contentInfo["config"].(map[string]interface{})
			variableInfos := make([]interface{}, 0)
			if contentInfo["variable"] != nil {
				variableInfos = contentInfo["variable"].([]interface{})
			}
			pdModel := models.SysDevProduct{}
			pdModel.Id = utils.GetStringValueFiled(pdInfo, "id")
			pdModel.Name = utils.GetStringValueFiled(pdInfo, "name")
			pdModel.DeviceType = utils.GetStringValueFiled(pdInfo, "deviceType")
			pdModel.Status = utils.GetBoolValueField(pdInfo, "status")
			pdModel.ProductModel = utils.GetStringValueFiled(pdInfo, "model")
			pdModel.DeptId = utils.GetStringValueFiled(pdInfo, "deptId")
			pdModel.Photo = utils.GetStringValueFiled(pdInfo, "photo")
			pdModel.Description = utils.GetStringValueFiled(pdInfo, "description")
			if pdInfo["view"] != nil {
				pdModel.View = utils.MapToBytes(pdInfo["view"].(map[string]interface{}))
			}
			now := cDto.LocalTime(time.Now())
			if pdInfo["createTime"] != nil {
				strTime, _ := utils.FormatStrTime(pdInfo["createTime"].(string))
				t := cDto.LocalTime(strTime)
				pdModel.CreateTime = &t
			} else {
				pdModel.CreateTime = &now
			}
			if pdInfo["updateTime"] != nil {
				strTime, _ := utils.FormatStrTime(pdInfo["updateTime"].(string))
				t := cDto.LocalTime(strTime)
				pdModel.UpdateTime = &t
			} else {
				pdModel.UpdateTime = &now
			}
			pdModel.Orm = tx
			// schema
			schemaModel := models.SysDevSchema{}
			schemaModel.Id = utils.GetStringValueFiled(schemaInfo, "id")
			schemaModel.DevSn = utils.GetStringValueFiled(schemaInfo, "devSn")
			schemaModel.ProductModel = utils.GetStringValueFiled(schemaInfo, "productModel")
			schemaModel.DevProductId = utils.GetStringValueFiled(schemaInfo, "devProductId")
			if schemaInfo["schema"] != nil {
				schemaModel.Schema = utils.MapToBytes(schemaInfo["schema"].(map[string]interface{}))
			}
			if schemaInfo["createTime"] != nil {
				strTime, _ := utils.FormatStrTime(schemaInfo["createTime"].(string))
				t := cDto.LocalTime(strTime)
				schemaModel.CreateTime = &t
			} else {
				schemaModel.CreateTime = &now
			}
			if schemaInfo["updateTime"] != nil {
				strTime, _ := utils.FormatStrTime(schemaInfo["updateTime"].(string))
				t := cDto.LocalTime(strTime)
				schemaModel.UpdateTime = &t
			} else {
				schemaModel.UpdateTime = &now
			}
			schemaModel.Orm = tx
			// config
			configModel := models.SysDevConfig{}
			configModel.Id = utils.GetStringValueFiled(configInfo, "id")
			configModel.DevSn = utils.GetStringValueFiled(configInfo, "devSn")
			configModel.ProductModel = utils.GetStringValueFiled(configInfo, "productModel")
			configModel.DevProductId = utils.GetStringValueFiled(configInfo, "devProductId")
			if configInfo["data"] != nil {
				configModel.Data = utils.MapToBytes(configInfo["data"].(map[string]interface{}))
			}
			if configInfo["createTime"] != nil {
				strTime, _ := utils.FormatStrTime(configInfo["createTime"].(string))
				t := cDto.LocalTime(strTime)
				configModel.CreateTime = &t
			} else {
				configModel.CreateTime = &now
			}
			if configInfo["updateTime"] != nil {
				strTime, _ := utils.FormatStrTime(configInfo["updateTime"].(string))
				t := cDto.LocalTime(strTime)
				configModel.UpdateTime = &t
			} else {
				configModel.UpdateTime = &now
			}
			configModel.Orm = tx
			// variable
			variableModel := models.SysProductVariable{}
			variableModel.Orm = tx
			variableDatas := make([]models.SysProductVariable, 0)
			for _, v := range variableInfos {
				variableInfo := v.(map[string]interface{})
				tmp := models.SysProductVariable{}
				tmp.ProductModel = utils.GetStringValueFiled(variableInfo, "productModel")
				tmp.Key = utils.GetStringValueFiled(variableInfo, "key")
				tmp.Label = utils.GetStringValueFiled(variableInfo, "label")
				tmp.Value = utils.GetStringValueFiled(variableInfo, "value")
				tmp.Type = utils.GetStringValueFiled(variableInfo, "type")
				if variableInfo["index"] != nil {
					index := utils.GetIntValueFiled(variableInfo, "index")
					tmp.Index = &index
				}
				if variableInfo["config"] != nil {
					tmp.Config = utils.MapToBytes(variableInfo["config"].(map[string]interface{}))
				}
				if variableInfo["param"] != nil {
					tmp.Param = utils.MapToBytes(variableInfo["param"].(map[string]interface{}))
				}
				if variableInfo["reportTime"] != nil {
					strTime, _ := utils.FormatStrTime(variableInfo["reportTime"].(string))
					t := cDto.LocalTime(strTime)
					tmp.ReportTime = &t
				} else {
					tmp.ReportTime = &now
				}
				if variableInfo["updateTime"] != nil {
					strTime, _ := utils.FormatStrTime(variableInfo["updateTime"].(string))
					t := cDto.LocalTime(strTime)
					tmp.UpdateTime = &t
				} else {
					tmp.UpdateTime = &now
				}
				variableDatas = append(variableDatas, tmp)
			}

			// 更新or新增
			// 按产品编码更新 认为相同产品编码 是同一产品
			txx := tx.Model(&models.SysDevProduct{}).Scopes(cDto.MakeCondition(query.SysProductQuery{ProductModel: pdModel.ProductModel})).Updates(&pdModel)
			if txx.Error != nil {
				return txx.Error
			}
			if txx.RowsAffected == 0 {
				if err = pdModel.Add(&pdModel); err != nil {
					return err
				}
				if err = schemaModel.Add(&schemaModel); err != nil {
					return err
				}
				if err = configModel.Add(&configModel); err != nil {
					return err
				}
				if len(variableDatas) > 0 {
					if err = variableModel.Upsert(&variableDatas, []string{"product_model", "key"}...); err != nil {
						return err
					}
				}
			} else {
				// 按产品编码更新 — 因为产品是按编码覆盖的
				tx.Model(&models.SysDevSchema{}).Scopes(cDto.MakeCondition(query.SysDevSchemaQuery{ProductModel: schemaModel.ProductModel})).Updates(&schemaModel)
				tx.Model(&models.SysDevConfig{}).Scopes(cDto.MakeCondition(query.SysDevConfigQuery{ProductModel: configModel.ProductModel})).Updates(&configModel)
				if len(variableDatas) > 0 {
					_ = variableModel.Upsert(&variableDatas, []string{"product_model", "key"}...)
				}
			}
		}
		return nil
	})
}
